package yawning.mew.character;

import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import yawning.mew.target.Target;

public class StatLogic {
	public synchronized static StatLogic get(int level, Clazz cl) {
		Cache cache = new Cache(level, cl);
		if (sCache.containsKey(cache)) return sCache.get(cache);
		StatLogic sl = new StatLogic(level, cl);
		sCache.put(cache, sl);
		return sl;
	}

	public static StatLogic get(Toon toon) {
		return get(toon.level, toon.mClass);
	}

	// Getters
	public double getDodgeConv() {
		return mDodgeConv;
	}

	public double getParryConv() {
		return mParryConv;
	}

	public double getBlockConv() {
		return mBlockConv;
	}

	public double getResilienceConv() {
		return mResilienceConv;
	}

	public double getCritConv() {
		return mCritConv;
	}

	public double getExpConv() {
		return mExpConv;
	}

	public double getHasteConv() {
		return mHasteConv;
	}

	public double getHitConv() {
		return mHitConv;
	}

	public double getSpellHitConv() {
		return mSpellHitConv;
	}

	public double getMasteryConv() {
		return mMasteryConv;
	}

	public double getPBaseMiss() {
		return mPBaseMiss;
	}

	public int getMeleeSpecialHitRatingCap(int targetLevel) {
		return (int) Math.ceil(Target.getMeleeSpecialMissRate(targetLevel, mLevel) * 100 * mHitConv);
	}

	public int getSpellHitRatingCap(int targetLevel) {
		return (int) Math.ceil(Target.getSpellMissRate(targetLevel, mLevel) * 100 * mHitConv);
	}

	public int getExpertiseRatingSoftCap(int targetLevel) {
		return (int) Math.ceil(Target.getDodgeRate(targetLevel, mLevel) * 100 * mExpConv);
	}

	public int getExpertiseRatingCap(int targetLevel) {
		return (int) Math.ceil(Target.getParryRate(targetLevel, mLevel) * 100 * mExpConv);
	}

	public double getBaseMastery() {
		return mBaseMastery;
	}

	public int getBaseStrength(Race race) {
		return race.getBaseStrength() + mStats.get("strength").intValue();
	}

	public int getBaseAgility(Race race) {
		return race.getBaseAgility() + mStats.get("agility").intValue();
	}

	public int getBaseStamina(Race race) {
		return race.getBaseStamina() + mStats.get("stamina").intValue();
	}

	public int getBaseIntellect(Race race) {
		return race.getBaseIntellect() + mStats.get("intellect").intValue();
	}

	public int getBaseSpirit(Race race) {
		return race.getBaseSpirit() + mStats.get("spirit").intValue();
	}

	public int getBaseHealth() {
		return mStats.get("health").intValue();
	}

	public int getBaseMana() {
		return mStats.get("mana").intValue();
	}

	public int getBaseAttackPower() {
		return (int) (mLevel * mBaseApLevelMult);
	}

	public double getBasePhysicalCrit() {
		return mBasePhysicalCrit;
	}

	public double getBaseSpellCrit() {
		return mBaseSpellCrit;
	}

	public double getBaseDodge() {
		return mBaseDodge;
	}

	public double getDodgeCap() {
		return mDodgeCap;
	}

	public double getBaseParry() {
		return mBaseParry;
	}

	public double getParryCap() {
		return mParryCap;
	}

	public double getBaseBlock() {
		return mBaseBlock;
	}

	public double getDiminishingReturnConstant() {
		return mDiminishingReturnConstant;
	}

	public int getBuffAgiStr() {
		return mBuffAgiStr;
	}

	public int getBuffStamina() {
		return mBuffStamina;
	}

	public int getBuffArmor() {
		return mBuffArmor;
	}

	public double getAgiCritConv() {
		return mAgiCritConv;
	}

	public double getAgiDodgeConv() {
		return mAgiDodgeConv;
	}

	public double getIntCritConv() {
		return mIntCritConv;
	}

	public double getStrParryConv() {
		return mStrParryConv;
	}

	private static class Cache {
		int mLevel;
		Clazz mClass;

		Cache(int level, Clazz cl) {
			mLevel = level;
			mClass = cl;
		}

		@Override
		public int hashCode() {
			return (mClass.mCid << 16) | mLevel;
		}

		@Override
		public boolean equals(Object o) {
			Cache c = (Cache)o;
			if (c == null) return false;
			return c.hashCode() == hashCode();
		}
	}

	private static Map<Cache,StatLogic> sCache = new HashMap<Cache,StatLogic>();
	private static final String STATLOGIC_FILE = "/META-INF/statlogic.xml";

	private StatLogic(int level, Clazz cl) {
		mLevel = level;
		mClass = cl;

		Map<String,Double> stats = new HashMap<String,Double>();

		try {
			SAXBuilder builder = new SAXBuilder();
			InputStream is = getClass().getResourceAsStream(STATLOGIC_FILE);
			Document doc = builder.build(is);
			Element root = doc.getRootElement();

			@SuppressWarnings("unchecked") List<Element> elements = root.getChildren();
			for (Element e : elements) {
				if ((e.getName().equals("level")
						&& (Integer.parseInt(e.getAttributeValue("value")) == level))
						|| (e.getName().equals("levelclass")
						&& (Integer.parseInt(e.getAttributeValue("level")) == level)
						&& e.getAttributeValue("class").equals(cl.toString()))
						|| (e.getName().equals("class")
						&& e.getAttributeValue("value").equals(cl.toString())))  {
					@SuppressWarnings("unchecked") List<Element> statElements = e.getChildren();
					for (Element el : statElements) {
						stats.put(el.getName(), Double.parseDouble(el.getAttributeValue("value")));
					}
				}
			}
		}
		catch (Exception e) {
			if (e instanceof RuntimeException) throw (RuntimeException)e;
			else throw new RuntimeException(e);
		}

		mStats = Collections.unmodifiableMap(stats);

		mBuffAgiStr = mStats.get("buffagistr").intValue();	// B. Shout. @ 85
		mBuffStamina = mStats.get("buffstamina").intValue();
		mBuffArmor = mStats.get("buffarmor").intValue();
		mAgiCritConv = mStats.get("agilityCritRatio");
		mAgiDodgeConv = mStats.get("agilityDodgeRatio");
		mIntCritConv = mStats.get("intellectCritRatio");
		mStrParryConv = mStats.get("strengthParryRatio");

		mBasePhysicalCrit = mStats.get("basePhysicalCrit");
		mBaseSpellCrit = mStats.get("baseSpellCrit");
		mBaseDodge = mStats.get("baseDodge");
		mDodgeCap = mStats.get("dodgeCap");
		mBaseParry = mStats.get("baseParry");
		mParryCap = mStats.get("parryCap");
		mBaseBlock = mStats.get("baseBlock");
		mDiminishingReturnConstant = mStats.get("diminishingReturnConstant");
		mBaseApLevelMult = mStats.get("baseApLevelMult");

		double h = mStats.get("h");
		mDodgeConv = 13.8 * h;
		mParryConv = 13.8 * h;
		mBlockConv = 6.9 * h;
		mHitConv = 9.37931 * h;		// HitR->1% Hit.
		mSpellHitConv = 8 * h;		// HitR->1% Spell Hit.
		mExpConv = 2.34483 * h * 4;	// ExpR->1% Dodge/Parry.
		mCritConv = 14 * h;			// CritR->1% Crit.
		mHasteConv = 10 * h;		// HasteR->1% Haste.
		mMasteryConv = 14 * h;		// Mastery Rating->1 Mastery
		mResilienceConv = mStats.get("resilienceConv");
	}

	private double mDodgeConv;
	private double mParryConv;
	private double mBlockConv;
	private double mHitConv;		// HitR->1% Hit.
	private double mSpellHitConv;	// HitR->1% Spell Hit.
	private double mExpConv;		// ExpR->1% Dodge/Parry.
	private double mCritConv;		// CritR->1% Crit.
	private double mHasteConv;		// HasteR->1% Haste. (Well; so much for the 1.3x buff in WotlK.)
	private double mMasteryConv;	// Mastery Rating->1 Mastery (????)
	private double mResilienceConv;
	private double mAgiCritConv;
	private double mAgiDodgeConv;
	private double mStrParryConv;
	private double mIntCritConv;
	private int mBuffAgiStr;		// Horn of Winter/Battle Shout Str/Agi
	private int mBuffStamina;
	private int mBuffArmor;
	private double mBasePhysicalCrit;
	private double mBaseSpellCrit;
	private double mBaseDodge;
	private double mDodgeCap;
	private double mBaseParry;
	private double mParryCap;
	private double mBaseBlock;
	private double mDiminishingReturnConstant;
	private double mBaseApLevelMult;

	private double mPBaseMiss = 0.05;
	private double mBaseMastery = 8.0;

	public final Clazz mClass;
	public final int mLevel;

	public final Map<String,Double> mStats;

	public static void main(String args[]) {
		StatLogic druid85 = new StatLogic(85, Clazz.DRUID);
		System.out.println(druid85.mStats);
	}
}
